// This file contains material supporting section 3.7 of the textbook:
// "Object Oriented Software Engineering" and is issued under the open-source
// license found at www.lloseng.com 

package controllers;
import messagesPacks.*;
import ocsf.client.*;

import java.io.*;


public class ChatClient extends ObservableSWRClient
{
	private int wait_retries = 0;
	private int max_retries = 2;
	public Object recieved_obj=null;

	/**
	 * Creates a new local client.
	 */
	public ChatClient() 
	{
		super("127.0.0.1", 0); //Call the superclass constructor
	}	  

	/**
	 * Creates a new client.
	 * @param host	The host to connect to.
	 * @param port  The port to connect to.
	 */
	public ChatClient(String host, int port) 
	{
		super(host, port); //Call the superclass constructor
    }
	
	/**
	 * Start a new connection to the server.
	 * @param host	The host to connect to.
	 * @param port  The port to connect to.
	 */	
	public void startConnection(String host, int port) throws IOException
	{
		setHost(host);
		setPort(port);
		openConnection();
	}

	/**
	 * This method handles all data that comes in from the server.
	 * @author Alon Shahaf
	 * @author Ariel Brucker
	 * @author Tali Pisarev
	 * @author Roman Faktorovich
	 * @param msg The message from the server.
	 */
	public void handleMessageFromServer(Object msg) 
	{
		// we should unpack the GenericMessage object to determine how to print the data stored within.
	  	GenericMessage new_msg;
	  	new_msg = (GenericMessage)msg;
	  	System.out.println("ChatClient: recived from server msg type = " + new_msg.getType());
	  	switch (new_msg.getType())
	  	{ 		

	  		case LibrarianGetBookSubjectMSG:
	  		{
	  			recieved_obj = (LibrarianSubjectMessage)new_msg;
	  			break;
	  		}
	  		
	  		case LibrarianRemoveCopyMSG:
	  		{
	  			recieved_obj = (LibrarianRemoveCopyMessage)new_msg;
	  			break;
	  		}
	  		
	  		case LibrarianRemoveBookMSG:
	  		{
	  			recieved_obj = (LibrarianRemoveBookMessage)new_msg;
	  			break;
	  		}
	  		
	  		case LibrarianReturnBookMSG:
	  		{
	  			recieved_obj = (LibrarianReturnBookMessage)new_msg;
	  			break;
	  		}
	  		
	  		case LibrarianEditBookMSG:
	  		{
	  			recieved_obj = (LibrarianEditBookMessage)new_msg;
	  			break;
	  		}
	  		
	  		case LibrarianEditCopyMSG:
	  		{
	  			recieved_obj = (LibrarianEditCopyMessage)new_msg;
	  			break;
	  		}
	  		
	  		case LibrarianAddCopyMSG:
	  		{
	  			recieved_obj = (LibrarianAddCopyMessage)new_msg;
	  			break;
	  		}
	  		case LibrarianSearchCopyMSG:
	  		{
	  			recieved_obj = (LibrarianSearchCopyMessage)new_msg;
	  			break;
	  		}
	  	
	  		case LibrarianSearchUserMSG:   
	  		{
	  			recieved_obj = (LibrarianSearchUserMessage)new_msg;
	  			break;
	  		}
	  		case OrderBookMSG:
	  		{
	  	    	recieved_obj = (BookOrderMessage)new_msg;
	  	    	break;
	  		}
	  		case LibrarianAddBookMSG:
	  		{
	  			recieved_obj = (LibrarianAddBookMessage)new_msg;
	  			break;
	  		}
	  		case ViewHistoryMSG:
	  		{
	  			recieved_obj = (ViewHistoryMessage)new_msg;
	  			
	  		}
	  		case SubscriberSearchBookMSG:
	  	    {
	  	    	recieved_obj = (SubscriberSearchBookMassege)new_msg;
	  	    	break;
	  	    }
	  	    case SubscUpdateProfileMSG:
	  	    {
	  	    	recieved_obj = (SubscriberUpdateProfileMessage)new_msg;
	  	    	break;
	  	    }
		  	case SubBorrowMSG:
		  	{
	  			recieved_obj = (SubscriberBorrowDataMessage)new_msg;
	  			break;
		  	}
		  	case SubExtendBorrowMSG:
		  	{
	  			recieved_obj = (SubExtendBorrowMessage)new_msg;
	  			break;
		  	}
	  		case BookSubjectsMSG:
	  		{
	  			recieved_obj = (BookSubjectMessage)new_msg;
	  			break;
  		    }
		  	case LoginMSG:
		  	{
		  		recieved_obj = (LoginMessage)new_msg;
		  		break;
		  	}
		  	case LogoutMSG:
		  	{
		  		recieved_obj = (LogoutMessage)new_msg;
		  		break;
		  	}		  	
			case Exit:
			{
				break;
			}
			case UsersMSG:
			{
				recieved_obj = (UsersMSG)new_msg;
				break;
			}
			case SimpleIntMSG:
			{
				recieved_obj = (SimpleIntMessage)new_msg;
				break;
			}
			case BorrowMSG:
			{
				recieved_obj = (BorrowMessage)new_msg;
				break;
			}
			case ManagerSerachSubMSG:
			{
				recieved_obj = (ManagerSerachSubMSG)new_msg;
				break;
			}
			case SuspendMSG:
			{
				recieved_obj = (SuspendMSG)new_msg;
				break;
			}
			case BorrowStatisticsMSG:
			{
				recieved_obj = (BorrowStatisticsMSG)new_msg;
				break;	
			}
			case ManagerLatenessReportMSG:
			{
				recieved_obj =(ManagerLatenessReportMSG)new_msg;
				break;
			}
			case ActivityMSG:
			{
				recieved_obj = (ActivityReportMessage)new_msg;
				break;
			}
			case TOCMSG:
			{
				recieved_obj = (TOCMessage)new_msg;
				break;
			}
			case DeleteTOCMSG:
			{
				recieved_obj = (DeleteTOCMessage)new_msg;
				break;
			}			
			default: break;
	  	}
	  	setChanged(); //Marks this Observable object as having been changed
	  	notifyAll();  //Wakes up all threads that are waiting on this object's monitor
	}

  /**
   * This method handles all data coming from the UI            
   * @author Alon Shahaf
   * @author Ariel Brucker
   * @author Tali Pisarev
   * @author Roman Faktorovich
   * @param message The message from the UI.    
   */
	public void handleMessageFromClientUI(Object message) throws IOException
	{
		sendToServer(message);
	}

  /**
   * This method terminates the client.
   */
	public void quit()
	{
		try
		{
			closeConnection();
		}
		catch(IOException e) {}
	}
  
	/**
	 * Sets how many retries to wait
	 * @param host	The host to connect to.
	 * @param port  The port to connect to.
	 */
	public void setWaitRetries(int wt)
	{
		this.wait_retries = wt;
	}
	
	/**
	 * get the number of wait retries.
	 * @return wait retries set to this client.
	 */
	public int getWaitRetries()
	{
		return this.wait_retries;
	}
	
	/**
	 * Sets the max retries to this client.
	 * @param mr	max retries.
	 */
	public void setMaxRetries(int mr)
	{
		this.max_retries = mr;
	}
	
	/**
	 * Get the max retries number.
	 * @return max retries number.
	 */	
	public int getMaxRetries()
	{
		return this.max_retries;
	}
	/**
	 * Sends a message to the server and waits for a specific response or timeout.
	 * @author Alon Shahaf
	 * @param message The message to be sent to the server
	 * @param expected_obj The type of message expected to be received from the server.
	 * @return The result message from the server.
	 */
	public synchronized Object sendToServerAndWait(Object message,Object expected_obj)
	{
		wait_retries=0;
		recieved_obj=null;
		
		clearAll();
		try 
		{
			System.out.println("ChatClient > sending to server MSG : "+ message.toString() );
			sendToServer(message);
		}
		catch (IOException e1) 
		{
			e1.printStackTrace();
			System.out.println("ChatClient: IOException!");
			return null;
		}

		while(true)
		{
			try
			{
				if ((recieved_obj != null) && (recieved_obj.getClass() == expected_obj.getClass()))
				{	
					return recieved_obj;
				}
				
				if (isCancelled()) 
				{
					System.out.println("ChatClient: CANCELED!");	
					return null;
				}
				System.out.println("ChatClient: waiting..");
				wait(6000);
				wait_retries++;
				// send Waiting to observers.
				setChanged(); //Marks this Observable object as having been changed
				notifyObservers(WAITING_FOR_REPLY);// notify all of its observers ,
				// Each observer has its update method called with two arguments: this observable object and the arg argument.
			}
			catch (InterruptedException e) 
			{
				//e.printStackTrace();
				System.out.println("ChatClient: InterruptedException!");
				return null;
			}
			catch (IllegalMonitorStateException e)
			{
				//e.printStackTrace();
				System.out.println("ChatClient: IllegalMonitorStateException!");
				return null;
			}
			catch (NullPointerException e)
			{
				System.out.println("ChatClient: NullPointerException!");
				return null;
			}
		}
	}
  
}
//End of ChatClient class
